﻿#include "map_gr_createprimitive.h"
#include <QDebug>
#include <osgEarthAnnotation/ImageOverlay>
#include <osgEarthAnnotation/CircleNode>
#include <osgEarthAnnotation/RectangleNode>
#include <osgEarthAnnotation/EllipseNode>
#include <osgEarthAnnotation/PlaceNode>
#include <osgEarthAnnotation/LabelNode>
#include <osgEarthAnnotation/LocalGeometryNode>
#include <osgEarthAnnotation/FeatureNode>
#include <osgEarthAnnotation/ModelNode>

#include <osgEarthAnnotation/AnnotationEditing>
#include <osgEarthAnnotation/ImageOverlayEditor>

#include <osgEarthSymbology/GeometryFactory>
#include "map_globaldata/map_globaldata_appdata.h"

MAP_GR_CreatePrimitive::MAP_GR_CreatePrimitive(const MAP_OSG_BaseHandler *pHandler, DrawingPrimitiveMode mode)
{
    this->m_pHandler = pHandler;
    this->m_mode = mode;

    m_points = new osg::Vec3dArray;
    m_pAnnoGroup = nullptr;
    m_pLabelGroup = nullptr;
    m_labelStyle.getOrCreate<osgEarth::TextSymbol>()->alignment() = osgEarth::TextSymbol::ALIGN_LEFT_CENTER;
    m_labelStyle.getOrCreate<osgEarth::TextSymbol>()->fill()->color() = osgEarth::Color::Red;
    m_labelStyle.getOrCreate<osgEarth::TextSymbol>()->size() = 20.0f;

    m_pathStyle.getOrCreate<osgEarth::LineSymbol>()->tessellationSize() = 1000;
    m_pathStyle.getOrCreate<osgEarth::PointSymbol>()->size() = 5;
    m_pathStyle.getOrCreate<osgEarth::PointSymbol>()->fill()->color() = osgEarth::Color::Red;
    m_pathStyle.getOrCreate<osgEarth::PointSymbol>()->smooth() = true;
    m_pathStyle.getOrCreate<osgEarth::AltitudeSymbol>()->clamping() = osgEarth::AltitudeSymbol::CLAMP_TO_TERRAIN;
    m_pathStyle.getOrCreate<osgEarth::AltitudeSymbol>()->technique() = osgEarth::AltitudeSymbol::TECHNIQUE_GPU;
    m_pathStyle.getOrCreate<osgEarth::RenderSymbol>()->depthOffset()->enabled() = true;

    m_isCalculate = false;
}

MAP_GR_CreatePrimitive::~MAP_GR_CreatePrimitive()
{
    if(m_points)
        m_points->clear();
}

void MAP_GR_CreatePrimitive::setMode(MAP_GR_CreatePrimitive::DrawingPrimitiveMode mode)
{
    this->m_mode = mode;
}

void MAP_GR_CreatePrimitive::beginHandle()
{
    auto a = MAP_GLOBALDATA_APPData::getInstance()->g_mapData->pHandler;
    connect(a, &MAP_OSG_BaseHandler::signalPicked, this, &MAP_GR_CreatePrimitive::slotPicked);
    connect(m_pHandler, &MAP_OSG_BaseHandler::signalMoving, this, &MAP_GR_CreatePrimitive::slotMoving);
    connect(m_pHandler, &MAP_OSG_BaseHandler::signalPickedXYZ, this, &MAP_GR_CreatePrimitive::slotPickedXYZ);
    connect(m_pHandler, &MAP_OSG_BaseHandler::signalMovingXYZ, this, &MAP_GR_CreatePrimitive::slotMovingXYZ);
    connect(m_pHandler, &MAP_OSG_BaseHandler::signalRightPicked, this, &MAP_GR_CreatePrimitive::slotRightHandle);
}

void MAP_GR_CreatePrimitive::endHandle()
{
    disconnect(m_pHandler, &MAP_OSG_BaseHandler::signalPicked, this, &MAP_GR_CreatePrimitive::slotPicked);
    disconnect(m_pHandler, &MAP_OSG_BaseHandler::signalMoving, this, &MAP_GR_CreatePrimitive::slotMoving);
    disconnect(m_pHandler, &MAP_OSG_BaseHandler::signalPickedXYZ, this, &MAP_GR_CreatePrimitive::slotPickedXYZ);
    disconnect(m_pHandler, &MAP_OSG_BaseHandler::signalMovingXYZ, this, &MAP_GR_CreatePrimitive::slotMovingXYZ);
    disconnect(m_pHandler, &MAP_OSG_BaseHandler::signalRightPicked, this, &MAP_GR_CreatePrimitive::slotRightHandle);
    m_points->clear();
}

void MAP_GR_CreatePrimitive::setCalculate(bool isCalculate)
{
    m_isCalculate = isCalculate;
}

void MAP_GR_CreatePrimitive::slotPicked(osg::Vec3d pos)
{
    //    qDebug() << "slot start:";
    m_points->push_back(pos);
    auto l_pMapData = MAP_GLOBALDATA_APPData::getInstance()->g_mapData;
    if(!l_pMapData->valid)
        return;
    if(m_pAnnoGroup)
    {
        l_pMapData->pMapNode->removeChild(m_pAnnoGroup);
        m_pAnnoGroup = nullptr;
        m_pLabelGroup = nullptr;
    }
    switch (m_mode)
    {
        case POINT:
            break;
        case LINE:
            break;
        case Polygon:
            drawPolygon();
            break;
        case ANGLE:
            if(m_points->size() == 4)
            {
                auto temp = m_points->back();
                m_points->clear();
                m_points->push_back(temp);
            }
            drawAngle();
            break;
    }
}

void MAP_GR_CreatePrimitive::slotMoving(osg::Vec3d pos)
{

}

void MAP_GR_CreatePrimitive::slotPickedXYZ(osg::Vec3d pos)
{

}

void MAP_GR_CreatePrimitive::slotMovingXYZ(osg::Vec3d pos)
{

}

void MAP_GR_CreatePrimitive::slotRightHandle()
{
    //    qDebug() << "right clicked:";
    auto l_pMapData = MAP_GLOBALDATA_APPData::getInstance()->g_mapData;
    if(!l_pMapData->valid)
        return;
    if(m_pAnnoGroup)
    {
        l_pMapData->pMapNode->removeChild(m_pAnnoGroup);
        m_pAnnoGroup = nullptr;
        m_pLabelGroup = nullptr;
    }
    m_points->clear();
}

void MAP_GR_CreatePrimitive::drawPolygon()
{
    auto l_pMapData = MAP_GLOBALDATA_APPData::getInstance()->g_mapData;
    if(!l_pMapData->valid)
        return;
    if(!m_pAnnoGroup)
    {
        m_pAnnoGroup = new osg::Group();
        m_pAnnoGroup->setName("annoGroup");
        l_pMapData->pMapNode->addChild( m_pAnnoGroup );
    }
    if(!m_pLabelGroup)
    {
        m_pLabelGroup = new osg::Group();
        m_pAnnoGroup->addChild( m_pLabelGroup );
    }

    osgEarth::Annotation::FeatureNode* pathNode = 0;
    {
        osgEarth::Geometry* path = new osgEarth::Polygon();
        for(auto i : *m_points)
            path->push_back(i);
        osgEarth::Features::Feature* pathFeature = new osgEarth::Features::Feature(path, l_pMapData->pMapSRS);
        pathFeature->geoInterp() = osgEarth::GEOINTERP_GREAT_CIRCLE;

        pathNode = new osgEarth::Annotation::FeatureNode(pathFeature, m_pathStyle);
        m_pAnnoGroup->addChild( pathNode );

        if(m_isCalculate)
        {
            double area = this->calculatePolygonArea();
            if(area == -1)
                return;
            area /= (1000 * 1000);
            osgEarth::Annotation::LabelNode* label = new osgEarth::Annotation::LabelNode(
                                                         (QString::number(area) + u8"km2").toStdString(), m_labelStyle);
            label->setPosition(osgEarth::GeoPoint(l_pMapData->pMapSRS, m_points->back().x(), m_points->back().y()));
            m_pLabelGroup->addChild(label);
        }
    }
}

double MAP_GR_CreatePrimitive::calculatePolygonArea()
{
    auto l_pMapData = MAP_GLOBALDATA_APPData::getInstance()->g_mapData;
    if(!l_pMapData->valid)
        return -1;
    if(m_points->size() < 3)
        return -1;
    double area = 0.0;
    osg::Vec3d first = m_points->at(0);
    auto geoSRS = l_pMapData->pMapSRS;
    geoSRS->transformToWorld(first, first);

    //利用扇形进行三角剖分（凹多边形不适用）
    for(unsigned long long i = 1; i < m_points->size() - 1; i++)
    {
        osg::Vec3d second = m_points->at(i);
        osg::Vec3d third = m_points->at(i + 1);
        geoSRS->transformToWorld(second, second);
        geoSRS->transformToWorld(third, third);

        area += calculateTriangleArea(first, second, third);
    }
    return area;
}

double MAP_GR_CreatePrimitive::calculateTriangleArea(const osg::Vec3 &a, const osg::Vec3 &b, const osg::Vec3 &c)
{
    double area = 0;

    double side[3];//存储三条边的长度;

    side[0] = sqrt(pow(a.x() - b.x(), 2) + pow(a.y() - b.y(), 2) + pow(a.z() - b.z(), 2));
    side[1] = sqrt(pow(a.x() - c.x(), 2) + pow(a.y() - c.y(), 2) + pow(a.z() - c.z(), 2));
    side[2] = sqrt(pow(c.x() - b.x(), 2) + pow(c.y() - b.y(), 2) + pow(c.z() - b.z(), 2));

    //不能构成三角形;
    if (side[0] + side[1] <= side[2] || side[0] + side[2] <= side[1] || side[1] + side[2] <= side[0])
        return area;

    //利用海伦公式。s=sqr(p*(p-a)(p-b)(p-c));
    double p = (side[0] + side[1] + side[2]) / 2; //半周长;
    area = sqrt(p*(p - side[0])*(p - side[1])*(p - side[2]));

    return area;
}

void MAP_GR_CreatePrimitive::drawAngle()
{
    auto l_pMapData = MAP_GLOBALDATA_APPData::getInstance()->g_mapData;
    if(!l_pMapData->valid)
        return;
    if(!m_pAnnoGroup)
    {
        m_pAnnoGroup = new osg::Group();
        m_pAnnoGroup->setName("annoGroup");
        l_pMapData->pMapNode->addChild( m_pAnnoGroup );
    }
    if(!m_pLabelGroup)
    {
        m_pLabelGroup = new osg::Group();
        m_pAnnoGroup->addChild( m_pLabelGroup );
    }

    osgEarth::Annotation::FeatureNode* pathNode = 0;
    {
        osgEarth::Geometry* path = new osgEarth::LineString();
        for(auto i : *m_points)
            path->push_back(i);
        osgEarth::Features::Feature* pathFeature = new osgEarth::Features::Feature(path, l_pMapData->pMapSRS);
        pathFeature->geoInterp() = osgEarth::GEOINTERP_GREAT_CIRCLE;

        pathNode = new osgEarth::Annotation::FeatureNode(pathFeature, m_pathStyle);
        m_pAnnoGroup->addChild( pathNode );

        if(m_isCalculate)
        {
            double degree = this->calculateDegree();
            if(degree == -1)
                return;
            osgEarth::Annotation::LabelNode* label = new osgEarth::Annotation::LabelNode(
                                                         (QString::number(degree) + u8"°").toStdString(), m_labelStyle);
            label->setPosition(osgEarth::GeoPoint(l_pMapData->pMapSRS, m_points->back().x(), m_points->back().y()));
            m_pLabelGroup->addChild(label);
        }
    }

}

double MAP_GR_CreatePrimitive::calculateDegree()
{
    if(m_points->size() != 3)
        return -1;
    auto degree1 = osgEarth::GeoMath::bearing(osg::DegreesToRadians(m_points->at(1).y()),
                                              osg::DegreesToRadians(m_points->at(1).x()),
                                              osg::DegreesToRadians(m_points->at(0).y()),
                                              osg::DegreesToRadians(m_points->at(0).x()));
    auto degree2 = osgEarth::GeoMath::bearing(osg::DegreesToRadians(m_points->at(1).y()),
                                              osg::DegreesToRadians(m_points->at(1).x()),
                                              osg::DegreesToRadians(m_points->at(2).y()),
                                              osg::DegreesToRadians(m_points->at(2).x()));
    return osg::RadiansToDegrees(abs(degree1 - degree2));
}

